import java.util.Comparator;

/*
 * Слова для индекса. Отличия от Word:
 * 1. Поддерживается морфологические преобразования - убираются все окончания. 
 * 2. Буква ё заменяется на е.
 * 3. Сравнения делаются не в лексиграфическом порядке, псевдолексографическом.
 * Цель изменения порядка - поиск по индексу слов без перебора всего дерева.
 */
final class WordIndex extends Word {

    final private static String[] endings = new String[] { "ими", "умя", "ами", "еми", "емя", "ого", "ому",
            "его", "ему", "ыми",
            "ас", "ах", "ая", "ее", "ей", "ем", "ех", "ам", "ею", "ую", "ех", "ие", "ий", "им", "ым",
            "их", "ию", "ми", "ум", "ух", "мя", "ов", "ое", "ой", "ом", "ою", "ый", "яя", "ые", "ых",
            "е", "у", "ю", "а", "я", "о", "и", "ы" };

    WordIndex(final String w, final boolean morph) {
        super((morph) ? morph(w) : w);
        toLower();
    }

    WordIndex(final WordUnicode w, final boolean morph) {
        super((morph) ? morph(w.toString()) : w.toString());
        toLower();
    }

    WordIndex(final byte[] v) {
        super(v);
    }

    /**
     * Отличие от лексиграфического порядка - считается, что все строки
     * одной длины. Недостающее забивается нулями. 
     * 
     * По дереву с таким порядок можно эффективно находит подстроки по запросу LIKE 'str%'
     * @param b - слово, с котором выполняется сравнение
     * @return - -1 если this<b, 0 - this=b, 1 - this>b
     */
    public int compareTo(WordInterface b) {
        final byte[] y = ((Word) b).value;
        final int length = Math.min(value.length, y.length);
        int cmp;
        for (int i = 0; i < length; i++) {
            if ((cmp = Byte.compare(value[i], y[i])) != 0) {
                return cmp;
            }
        }
        return Integer.compare(value.length, y.length);
    }

    /**
     * 
     * @param - b слово, с которым производтся сравнение
     * @return -1 если символ в value на позиции pos меньше в, чем символ в b
     */
    int compareToPrefix(WordInterface b) {
        final byte[] y = ((Word) b).value;
        final int length = Math.min(value.length, y.length);
        int cmp;
        for (int i = 0; i < length; i++) {
            if ((cmp = Byte.compare(value[i], y[i])) != 0) {
                return cmp;
            }
        }
        if (value.length >= y.length)
            return 0;
        return -1;
    }

    /**
     * Проверяет начинается ли строка с указанного префикса
     * @param prefix - префикс, который должен быть сопоставлен
     * @returnвозвращает значение true, если параметр prefix 
     * является префиксом данной строки; в противном случае значение false.
     */
    public boolean startsWith(WordIndex prefix) {
        if (prefix.length() > length()) {
            return false;
        }
        for (int i = 0; i < length(); i++) {
            if (value[i] != prefix.value[i])
                return false;
        }
        return true;
    }

    /**
     * Выполнить морфологические преобразование сроки. В преобразовании:
     * 1. Удаляются окончания существительных и прилагательных
     * 2. Буква ё заменяется на е
     * @param str - исходная строка
     * @return - преобразованная строка
     */
    private static String morph(String str) {
        for (String ending : endings) {
            if (str.length() > ending.length()+3 && str.endsWith(ending)) {     // 3 буквы в слове + окончание
                String res = str.substring(0, str.length() - ending.length());
                return res.replace('ё', 'е');
            }
        }
        return str.replace('ё', 'е');
    }

    /**
     * Перевод символа в 1251 в нижний регистр
     */
    private void toLower() {
        for (int i = 0; i < value.length; i++) {
            byte v = value[i];
            if (v >= (byte) 0xC0 && v <= (byte) 0xDF || v >= (byte) 0x41 && v <= (byte) 0x5A)
                value[i] += 0x20;
        }
    }

    /**
     * Компаратор для сортировки слов при поиске Like2
     * Слова, которые в дереве расположены глубже должны идти вначале списка поиска
     * Это оптимизация, чтобы уменьшить перебор дерева индекса
     */
    static class AVLTreePositionComparator implements Comparator<WordInterface> {

        AVLTree<Iterable<Node<RefData>>> tree;
        AVLTreePositionComparator(AVLTree<Iterable<Node<RefData>>> tree) {
            this.tree = tree;
        }

        public int compare(WordInterface w1, WordInterface w2) {
            if (!(w1 instanceof WordIndex))
                return 1;
            if (!(w2 instanceof WordIndex))
                return -1;
            int c1 = tree.getSubtreeHeight((WordIndex) w1);
            int c2 = tree.getSubtreeHeight((WordIndex) w2);
            return Integer.compare(c2, c1);     // reverse order
        }
    }
}